﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using YArchitech.Revit;
using YJKArch.Utils;

namespace YJKArch.WallModule.WallAlignmentToBeam
{
	public class AlignmentKit
	{
		public AlignmentKit(ExternalCommandData cmdData)
		{
			this.m_Revit = cmdData;
			this.m_doc = this.m_Revit.Application.ActiveUIDocument.Document;
		}

		public Result Alignment(ref string message)
		{
			ElementId oldBaselineValue = null;
			this.HideBaseline(ref oldBaselineValue);
			List<Wall> list = new List<Wall>();
			if (!this.SelectWalls(ref list))
			{
				this.UnHideBaseLine(oldBaselineValue);
				return Autodesk.Revit.UI.Result.Cancelled;
			}
			this.UnHideBaseLine(oldBaselineValue);
			List<double> list2 = new List<double>();
			this.ExtendWall(list, ref list2);
			Transaction transaction = new Transaction(this.m_doc);
			transaction.Start("wall alignment to beam");
			for (int i = 0; i < list.Count; i++)
			{
				try
				{
					this.AlignmentOne(list[i], list2[i]);
				}
				catch (Exception)
				{
				}
			}
			transaction.Commit();
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		private void HideBaseline(ref ElementId oldBaselineValue)
		{
			Transaction transaction = null;
			try
			{
				transaction = new Transaction(this.m_doc);
				transaction.Start("HideBaseline");
				Parameter underLayParameter = (this.m_Revit.Application.ActiveUIDocument.ActiveView as Autodesk.Revit.DB.ViewPlan).GetUnderLayParameter();
				ElementId elementId = underLayParameter.AsElementId();
				oldBaselineValue = elementId;
				underLayParameter.Set(ElementId.InvalidElementId);
				transaction.Commit();
			}
			catch (Exception)
			{
				if (transaction != null)
				{
					transaction.RollBack();
				}
			}
		}

		private void UnHideBaseLine(ElementId oldBaselineValue)
		{
			Transaction transaction = null;
			try
			{
				transaction = new Transaction(this.m_doc);
				transaction.Start("UnHideBaseline");
				(this.m_Revit.Application.ActiveUIDocument.ActiveView as Autodesk.Revit.DB.ViewPlan).GetUnderLayParameter().Set(oldBaselineValue);
				transaction.Commit();
			}
			catch (Exception)
			{
				if (transaction != null)
				{
					transaction.RollBack();
				}
			}
		}

		private void ExtendWall(List<Wall> walls, ref List<double> oldValue)
		{
			Transaction transaction = new Transaction(this.m_doc);
			transaction.Start("ExtendWall");
			foreach (Wall wall in walls)
			{
				try
				{
					if (wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId() == ElementId.InvalidElementId)
					{
						double num = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble();
						oldValue.Add(num);
						wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(num + this.m_TopOffset);
					}
					else
					{
						double num2 = wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).AsDouble();
						oldValue.Add(num2);
						wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(num2 + this.m_TopOffset);
					}
				}
				catch (Exception)
				{
				}
			}
			transaction.Commit();
		}

		private bool SelectWalls(ref List<Wall> walls)
		{
			IList<Reference> list = null;
			Selection selection = this.m_Revit.Application.ActiveUIDocument.Selection;
			try
			{
				list = selection.PickObjects(Autodesk.Revit.UI.Selection.ObjectType.Element, new YJKArch.WallModule.WallFilter(), "选择墙");
			}
			catch (Exception)
			{
				return false;
			}
			int num = 0;
			new List<Curve>();
			foreach (Reference reference in list)
			{
				Wall wall = this.m_doc.GetElement(reference) as Wall;
				if (wall != null)
				{
					if (wall.get_Parameter(BuiltInParameter.WALL_TOP_IS_ATTACHED).AsInteger() == 1)
					{
						num++;
					}
					else
					{
						walls.Add(wall);
					}
				}
			}
			if (num > 0)
			{
				MessageBox.Show("有" + num.ToString() + "个已附着顶部的墙未进行处理", "盈建科提示");
			}
			return walls.Count != 0;
		}

		private bool AlignmentOne(Wall wall, double oldValue)
		{
			double wallBottomElevation = this.GetWallBottomElevation(wall);
			ElementId elementId = wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId();
			if (elementId == ElementId.InvalidElementId)
			{
				double num = 0.0;
				if (!this.FindIntersectBeam(wall, ref num))
				{
					wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(oldValue);
				}
				else
				{
					double num2 = num - wallBottomElevation;
					if (Geometry.Lessthan_Or_Equal(num2, 0.0))
					{
						wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(oldValue);
					}
					else
					{
						wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(num2);
					}
				}
			}
			else
			{
				Level level = this.m_doc.GetElement(elementId) as Level;
				double num3 = 0.0;
				if (!this.FindIntersectBeam(wall, ref num3))
				{
					wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(oldValue);
				}
				else if (Geometry.Lessthan_Or_Equal(num3 - wallBottomElevation, 0.0))
				{
					wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(oldValue);
				}
				else
				{
					double num4 = num3 - level.Elevation;
					wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(num4);
				}
			}
			return true;
		}

		private bool FindIntersectBeam(Wall wall, ref double elevation)
		{
			bool result;
			try
			{
				Solid wallSolid = this.GetWallSolid(wall);
				if (null == wallSolid)
				{
					result = false;
				}
				else
				{
					ElementIntersectsSolidFilter elementIntersectsSolidFilter = new ElementIntersectsSolidFilter(wallSolid);
					IEnumerable<Element> enumerable = new FilteredElementCollector(this.m_doc).WhereElementIsCurveDriven().OfCategory(BuiltInCategory.OST_StructuralFraming).WherePasses(elementIntersectsSolidFilter).ToElements();
					bool flag = false;
					double num = double.MaxValue;
					foreach (Element element in enumerable)
					{
						if (!(element.GetType() != typeof(FamilyInstance)))
						{
							ElementId elementId = element.get_Parameter(BuiltInParameter.INSTANCE_REFERENCE_LEVEL_PARAM).AsElementId();
							Level level = this.m_doc.GetElement(elementId) as Level;
							double num2 = element.get_Parameter(BuiltInParameter.STRUCTURAL_BEAM_END0_ELEVATION).AsDouble();
							double val = element.get_Parameter(BuiltInParameter.STRUCTURAL_BEAM_END1_ELEVATION).AsDouble();
							if (Geometry.IsEqual(num2, val))
							{
								double num3 = element.get_Parameter(BuiltInParameter.Z_OFFSET_VALUE).AsDouble();
								double num4 = this.GetBeamHeight(element as FamilyInstance);
								int num5 = element.get_Parameter(BuiltInParameter.Z_JUSTIFICATION).AsInteger();
								if (num5 == 3)
								{
									num4 = 0.0;
								}
								else if (num5 == 1)
								{
									num4 *= 0.5;
								}
								double num6 = level.Elevation + num2 + num3 - num4;
								flag = true;
								if (Geometry.GreaterThan(num, num6))
								{
									num = num6;
								}
							}
						}
					}
					if (flag)
					{
						elevation = num;
						result = true;
					}
					else
					{
						result = false;
					}
				}
			}
			catch (Exception)
			{
				result = false;
			}
			return result;
		}

		private double GetWallBottomElevation(Wall wall)
		{
			double result;
			try
			{
				ElementId elementId = wall.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).AsElementId();
				if (elementId == ElementId.InvalidElementId)
				{
					result = 0.0;
				}
				else
				{
					Level level = this.m_doc.GetElement(elementId) as Level;
					double num = wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).AsDouble();
					result = level.Elevation + num;
				}
			}
			catch (Exception)
			{
				result = 0.0;
			}
			return result;
		}

		private Solid GetWallSolid(Wall wall)
		{
			Solid result;
			try
			{
				Options options = this.m_Revit.Application.Application.Create.NewGeometryOptions();
				options.DetailLevel = this.m_Revit.Application.ActiveUIDocument.ActiveView.DetailLevel;
				options.ComputeReferences = true;
				GeometryElement geometryElement = wall.get_Geometry(options);
				if (null == geometryElement)
				{
					result = null;
				}
				else
				{
					foreach (GeometryObject geometryObject in geometryElement)
					{
						if (!(geometryObject.GetType() != typeof(Solid)))
						{
							Solid solid = geometryObject as Solid;
							if (solid != null && Geometry.GreaterThan(solid.Volume, 0.0))
							{
								return solid;
							}
						}
					}
					result = null;
				}
			}
			catch (Exception)
			{
				result = null;
			}
			return result;
		}

		private double GetBeamHeight(FamilyInstance beam)
		{
			double result;
			try
			{
				Options options = this.m_Revit.Application.Application.Create.NewGeometryOptions();
				options.DetailLevel = (ViewDetailLevel)3;
				options.ComputeReferences = true;
				List<Solid> list = new List<Solid>();
				GeometryElement geometryElement = beam.get_Geometry(options);
				if (null == geometryElement)
				{
					result = 0.0;
				}
				else
				{
					List<double> list2 = new List<double>();
					foreach (GeometryObject geometryObject in RevitVersionFuncs.GetGeometryObjectArray(geometryElement))
					{
						if (geometryObject.GetType() == typeof(Solid))
						{
							Solid solid = geometryObject as Solid;
							if (solid != null && Geometry.GreaterThan(solid.Volume, 0.0))
							{
								list.Add(solid);
								continue;
							}
						}
						GeometryInstance geometryInstance = geometryObject as GeometryInstance;
						if (!(null == geometryInstance))
						{
							foreach (GeometryObject geometryObject2 in RevitVersionFuncs.GetGeometryObjectArray(geometryInstance.GetInstanceGeometry()))
							{
								Solid solid2 = geometryObject2 as Solid;
								if (solid2 != null && Geometry.GreaterThan(solid2.Volume, 0.0))
								{
									list.Add(solid2);
								}
							}
						}
					}
					foreach (Solid solid3 in list)
					{
						foreach (object obj in solid3.Faces)
						{
							Face face = (Face)obj;
							XYZ xyz = face.ComputeNormal(new UV(0.0, 0.0));
							if (xyz.IsAlmostEqualTo(XYZ.BasisZ) || xyz.IsAlmostEqualTo(-XYZ.BasisZ))
							{
								try
								{
                                    Autodesk.Revit.DB.Edge edge = face.EdgeLoops.get_Item(0).get_Item(0);
									IntersectionResult intersectionResult = face.Project(edge.AsCurve().GetEndPoint(0));
									list2.Add(intersectionResult.XYZPoint.Z);
								}
								catch (Exception)
								{
								}
							}
						}
					}
					if (list2.Count < 2)
					{
						result = 0.0;
					}
					else
					{
						list2.Sort();
						result = Math.Abs(list2[list2.Count - 1] - list2[0]);
					}
				}
			}
			catch (Exception)
			{
				result = 0.0;
			}
			return result;
		}

		private ExternalCommandData m_Revit;

		private Autodesk.Revit.DB.Document m_doc;

		private double m_TopOffset = Common.MMToFeet(1500.0);
	}
}
